use super::*;

/// Extracts arguments from JavaScript function calls
#[derive(Clone, Copy)]
pub struct ArgsExtracter<'val> {
	env: &'val Env,
	inner: sys::napi_callback_info,
}

impl<'val> ArgsExtracter<'val> {
	/// Creates a new ArgsExtracter from callback info
	pub fn new<'env>(env: &'env Env, info: sys::napi_callback_info) -> ArgsExtracter<'env> {
		ArgsExtracter { env, inner: info }
	}

	/// Gets the new.target value from constructor calls
	pub fn get_new_target(&'_ self) -> Result<'static, Value<'val>> {
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_get_new_target(self.env.raw(), self.inner, &mut ret) }.result("_")?;
		Ok(Value::new(self.env, ret))
	}
}

/// Macro to implement tuple conversion for function arguments
macro_rules! tuple_impl_serial {
	($len:tt $($n:tt $name:ident)*) => {
		impl<'val, $($name:FromNapi<'val>,)*> TryFrom<ArgsExtracter<'val>> for (Value<'val>,*mut core::ffi::c_void,$($name,)*) {
			type Error = Status<'val>;
			fn try_from(value: ArgsExtracter<'val>) -> std::result::Result<Self, Self::Error> {
				let env = value.env;
				let mut argc = $len;
				let mut argv: [sys::napi_value; $len] = [core::ptr::null_mut(); $len];
				let mut this = core::ptr::null_mut();
				let mut data = core::ptr::null_mut();
				unsafe {
					sys::napi_get_cb_info(
						env.raw(),
						value.inner,
						&mut argc,
						argv.as_mut_ptr(),
						&mut this,
						&mut data,
					)
				}
				.result("napi_get_cb_info")?;
				Ok((
					Value::new(env, this),
					data,
					$($name::from_napi(Value::new(env, argv[$n]))?,)*
				))
			}
		}
	}
}

// Generate tuple implementations for 0 to 20 arguments
tuple_impl_serial!(0);
tuple_impl_serial!(1 0 A);
tuple_impl_serial!(2 0 A 1 B);
tuple_impl_serial!(3 0 A 1 B 2 C);
tuple_impl_serial!(4 0 A 1 B 2 C 3 D);
tuple_impl_serial!(5 0 A 1 B 2 C 3 D 4 E);
tuple_impl_serial!(6 0 A 1 B 2 C 3 D 4 E 5 F);
tuple_impl_serial!(7 0 A 1 B 2 C 3 D 4 E 5 F 6 G);
tuple_impl_serial!(8 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H);
tuple_impl_serial!(9 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I);
tuple_impl_serial!(10 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J);
tuple_impl_serial!(11 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K);
tuple_impl_serial!(12 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L);
tuple_impl_serial!(13 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M);
tuple_impl_serial!(14 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N);
tuple_impl_serial!(15 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O);
tuple_impl_serial!(16 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O 15 P);
tuple_impl_serial!(17 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O 15 P 16 Q);
tuple_impl_serial!(18 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O 15 P 16 Q 17 R);
tuple_impl_serial!(19 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O 15 P 16 Q 17 R 18 S);
tuple_impl_serial!(20 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O 15 P 16 Q 17 R 18 S 19 T);
